//! src/mem/mem.rs
//! 
//! # RISC-V 内存模拟模块
//!
//! 这个模块提供了`Memory`结构体，用于模拟RISC-V处理器的主内存。
//! 它支持:
//! - 在指定的基地址和大小下创建内存空间。
//! - 从二进制文件加载程序镜像。
//! - 遵循RISC-V对齐规则的8位、16位和32位读写操作。
//! - 丰富的错误处理，能够明确区分地址越界和地址未对齐等错误。

// 引入我们自定义的错误类型，这是模块间协作的第一步。
use crate::common::SimError;

/// RISC-V 内存模拟结构体
///
/// `Memory` 结构体在内部使用一个 `Vec<u8>` 来存储数据，模拟一个线性的字节地址空间。
pub struct Memory {
    /// 内存空间的起始地址（基地址）。
    pub base: u32,
    /// 内存空间的总大小（字节）。
    pub size: u32,
    /// 存储实际内存数据的字节向量。
    data: Vec<u8>,
}

impl Memory {
    /// 创建一个新的内存模块实例。
    ///
    /// # Parameters
    /// * `base`: 内存的基地址，例如 `0x8000_0000`。
    /// * `size`: 内存的大小，以字节为单位。
    ///
    /// # Returns
    /// 返回一个用零初始化的 `Memory` 实例。
    pub fn new(base: u32, size: u32) -> Self {
        Memory {
            base,
            size,
            data: vec![0; size as usize],
        }
    }

    /// 将二进制镜像加载到内存的指定地址。
    ///
    /// # Parameters
    /// * `base_addr`: 镜像要被加载到的起始内存地址。
    /// * `image`: 一个包含二进制数据的字节切片 `&[u8]`。
    ///
    /// # Errors
    /// 如果加载地址超出内存范围，或者镜像太大无法容纳，
    /// 将返回 `SimError::AddressOutOfBounds` 错误。
    pub fn load_image(&mut self, base_addr: u32, image: &[u8]) -> Result<(), SimError> {
        // 1. 计算相对于内部 Vec 的偏移量，如果基地址无效则返回错误。
        let offset = base_addr
            .checked_sub(self.base)
            .ok_or(SimError::AddressOutOfBounds(base_addr))? as usize;

        // 2. 检查镜像是否能被完整地放入内存。
        if offset + image.len() > self.data.len() {
            // 计算溢出的确切地址，提供更精确的错误信息。
            let overflow_addr = base_addr.wrapping_add(image.len() as u32);
            return Err(SimError::AddressOutOfBounds(overflow_addr));
        }

        // 3. 使用 copy_from_slice 高效地将数据复制到内存中。
        self.data[offset..offset + image.len()].copy_from_slice(image);
        
        // 4. 使用 log crate 记录一条信息，这对于调试非常有用。
        log::info!(
            "成功加载 {} 字节到 0x{:08X} - 0x{:08X}",
            image.len(),
            base_addr,
            base_addr.wrapping_add(image.len() as u32) - 1
        );
        Ok(())
    }

    // --- 符合 Rust 风格和 RISC-V 规范的内存访问 API ---

    /// 从内存读取一个32位无符号整数（word）。
    ///
    /// # Parameters
    /// * `addr`: 要读取的内存地址。
    ///
    /// # Returns
    /// 返回小端序的 `u32` 数据。
    ///
    /// # Errors
    /// - `SimError::AddressOutOfBounds`: 如果地址超出内存边界。
    /// - `SimError::AddressMisaligned`: 如果地址不是4的倍数。
    pub fn read_u32(&self, addr: u32) -> Result<u32, SimError> {
        // 1. 先检查地址是否越界。
        let offset = self.get_offset(addr, 4)?;
        // 2. 再检查地址是否对齐。
        self.check_alignment(addr, 4)?;
        let bytes = self.data[offset..offset + 4]
            .try_into()
            .expect("切片长度已检查，应为4");
        Ok(u32::from_le_bytes(bytes))
    }

    /// 向内存写入一个32位无符号整数（word）。
    pub fn write_u32(&mut self, addr: u32, value: u32) -> Result<(), SimError> {
        self.check_alignment(addr, 4)?;
        let offset = self.get_offset(addr, 4)?;
        self.data[offset..offset + 4].copy_from_slice(&value.to_le_bytes());
        Ok(())
    }

    /// 从内存读取一个16位无符号整数（half-word）。
    pub fn read_u16(&self, addr: u32) -> Result<u16, SimError> {
        let offset = self.get_offset(addr, 2)?;
        self.check_alignment(addr, 2)?;
        let bytes = self.data[offset..offset + 2]
            .try_into()
            .expect("切片长度已检查，应为2");
        Ok(u16::from_le_bytes(bytes))
    }

    /// 向内存写入一个16位无符号整数（half-word）。
    pub fn write_u16(&mut self, addr: u32, value: u16) -> Result<(), SimError> {
        self.check_alignment(addr, 2)?;
        let offset = self.get_offset(addr, 2)?;
        self.data[offset..offset + 2].copy_from_slice(&value.to_le_bytes());
        Ok(())
    }

    /// 从内存读取一个8位无符号整数（byte）。
    pub fn read_u8(&self, addr: u32) -> Result<u8, SimError> {
        // 单字节访问永远是对齐的，无需检查。
        let offset = self.get_offset(addr, 1)?;
        Ok(self.data[offset])
    }
    
    /// 向内存写入一个8位无符号整数（byte）。
    pub fn write_u8(&mut self, addr: u32, value: u8) -> Result<(), SimError> {
        let offset = self.get_offset(addr, 1)?;
        self.data[offset] = value;
        Ok(())
    }

    // --- 内部辅助函数 (模块私有) ---

    /// 检查地址是否满足对齐要求。
    #[inline]
    fn check_alignment(&self, addr: u32, size: usize) -> Result<(), SimError> {
        if addr % size as u32 != 0 {
            Err(SimError::AddressMisaligned { addr, size })
        } else {
            Ok(())
        }
    }

    /// 根据地址计算内部 Vec 的偏移量，并同时检查边界。
    #[inline]
    fn get_offset(&self, addr: u32, size: usize) -> Result<usize, SimError> {
        let offset = addr
            .checked_sub(self.base)
            .ok_or(SimError::AddressOutOfBounds(addr))? as usize;
        
        // 检查访问的末尾是否越界
        if offset + size > self.data.len() {
            return Err(SimError::AddressOutOfBounds(addr));
        }
        Ok(offset)
    }

    // 获取数据切片的引用
    pub fn get_data_slice(&self) -> &[u8] {
        &self.data
    }

    pub fn get_data_slice_mut(&mut self) -> &mut [u8] {
        &mut self.data
    }

}

/// 实现 Debug trait 以便能用 {:?} 或 {:#?} 打印内存的十六进制视图。
/// 这对于调试来说至关重要。
impl std::fmt::Debug for Memory {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let mut data_hex = String::new();
        // 为了性能，只打印内存的前256字节
        const DUMP_LIMIT: usize = 256;
        for (i, byte) in self.data.iter().take(DUMP_LIMIT).enumerate() {
            if i % 16 == 0 {
                data_hex.push_str(&format!("\n0x{:08X}: ", self.base + i as u32));
            }
            data_hex.push_str(&format!("{:02X} ", byte));
        }
        if self.data.len() > DUMP_LIMIT {
            data_hex.push_str("\n...");
        }

        write!(
            f,
            "Memory {{\n  base: 0x{:08X},\n  size: {} bytes,\n  data: {}\n}}",
            self.base, self.data.len(), data_hex
        )
    }
}


#[cfg(test)]
mod tests {
    use super::*;
    use crate::common::SimError;
    use std::fs;
    use std::path::Path;

    #[test]
    fn test_read_write_cycle() {
        let mut memory = Memory::new(0x8000_0000, 1024);
        // 测试 u32
        memory.write_u32(0x8000_0004, 0xDEADBEEF).unwrap();
        assert_eq!(memory.read_u32(0x8000_0004).unwrap(), 0xDEADBEEF);
        // 测试 u16
        memory.write_u16(0x8000_000A, 0x1234).unwrap();
        assert_eq!(memory.read_u16(0x8000_000A).unwrap(), 0x1234);
        // 测试 u8
        memory.write_u8(0x8000_000F, 0xAB).unwrap();
        assert_eq!(memory.read_u8(0x8000_000F).unwrap(), 0xAB);
    }

    #[test]
    fn test_address_out_of_bounds() {
        let memory = Memory::new(0x8000_0000, 1024);
        // 尝试读取正好在边界外的地址
        let addr_high = 0x8000_0000 + 1024;
        assert_eq!(memory.read_u32(addr_high).unwrap_err(), SimError::AddressOutOfBounds(addr_high));
        // 尝试读取低于基地址的地址
        let addr_low = 0x7FFFFFFF;
        assert_eq!(memory.read_u32(addr_low).unwrap_err(), SimError::AddressOutOfBounds(addr_low));
    }

    #[test]
    fn test_address_misaligned() {
        let memory = Memory::new(0x8000_0000, 1024);
        // u32 对齐测试
        let addr32 = 0x8000_0005;
        assert_eq!(memory.read_u32(addr32).unwrap_err(), SimError::AddressMisaligned { addr: addr32, size: 4 });
        // u16 对齐测试
        let addr16 = 0x8000_0007;
        assert_eq!(memory.read_u16(addr16).unwrap_err(), SimError::AddressMisaligned { addr: addr16, size: 2 });
    }

    /// 集成测试：加载并验证特定的 `update-x-bin` 文件。
    ///
    /// 注意：此测试依赖于本地文件系统上的一个特定文件。
    /// 在CI/CD环境或没有此文件的机器上，此测试会被跳过。
    #[test]
    fn test_load_and_verify_specific_bin_file() {
        let bin_path_str = "D:\\Users\\92439\\Documents\\00_ThinkB16P\\04_RiscV\\update-x-bin";
        let bin_path: &Path = Path::new(bin_path_str);

        // 只在文件存在时运行测试，避免在其他环境下失败。
        if !bin_path.exists() {
            // 使用 eprintln! 在测试输出中打印警告信息。
            eprintln!("\n警告：测试文件 '{}' 不存在，跳过此测试。", bin_path_str);
            return;
        }

        // 1. 创建内存实例
        let mut memory = Memory::new(0x8000_0000, 2 * 1024 * 1024);
        
        // 2. 读取并加载BIN文件
        let bin_data = fs::read(bin_path).expect("无法读取BIN文件");
        memory.load_image(0x8000_0000, &bin_data).expect("内存加载失败");

        // 3. 验证内存中的关键指令（小端序）
        // 根据你之前的分析，第一条指令是 `addi a0, zero, 420` -> 0x1A400513
        // 经过确认，你的原始指令 `0x02A00513` 应该是 `addi a0, x0, 42`
        assert_eq!(memory.read_u32(0x8000_0000).unwrap(), 0x02A00513);
        
        // 第二条指令 `jal ra, 24` -> `0x018000EF`
        assert_eq!(memory.read_u32(0x8000_0004).unwrap(), 0x018000EF);

        // 打印内存快照以供人工核对
        println!("加载 BIN 文件后的内存快照: {:#?}", memory);
    }
}
